//
// Created by cljh on 2023/3/28.
//

#ifndef BIN_PACKING_CLIPPERUTIL_H
#define BIN_PACKING_CLIPPERUTIL_H


#include "../entity/NestPath.h"
#include "../lib/Clipper2Lib/include/clipper2/clipper.h"\

class ClipperUtil{
    //OGC关于数据格式有规定，多边形的外环应为逆时针，内环应为顺时针
    //多边形面积求和，使用鞋带公式：环的节点序号从1开始，第1点与第n点相同，如果结果S大于0，说明是逆时针，如果结果S小于0，说明是顺时针。
public:
    static bool ccw(Polygon *polygon){//是否为逆时针
        double s=0;
        for(int i=0;i<polygon->lenth;i++){
            Point pi=polygon->getPoint(i);
            Point ppi;
            if(i==polygon->lenth-1){
                ppi=polygon->getPoint(0);
            }else{
                ppi=polygon->getPoint(i+1);
            }
            s += pi.x*ppi.y - pi.y*ppi.x;
        }
        if(s>0){
            return true;
        }else{
            return false;
        }
    }
    static NestPath unionPolygons(std::vector<NestPath> polygons,std::vector<Vector> placements){
        if(polygons.size()==0||placements.size()==0){
            std::cout<<"something goes wrong in union "<<polygons.size();
        }
        Clipper2Lib::PathsD subject, clip, solution;
        for(int i=0;i<polygons.size();i++){
            NestPath nestPath=polygons[i];
            Vector offset=placements[i];
            for(int j=0;j<nestPath.size();j++){
                Polygon *polygon=nestPath[j];
                std::vector<double> pointVector=std::vector<double>();
                if(ccw(polygon)){
                    for(int k=0;k<polygon->lenth;k++){
                        pointVector.push_back(polygon->getx(k)+offset.x);
                        pointVector.push_back(polygon->gety(k)+offset.y);
                    }
                }else{
                    for(int k=polygon->lenth-1;k>=0;k--){
                        pointVector.push_back(polygon->getx(k)+offset.x);
                        pointVector.push_back(polygon->gety(k)+offset.y);
                    }
                }
                subject.push_back(Clipper2Lib::MakePathD(pointVector));
            }
        }
        solution = Clipper2Lib::Union(subject, clip, Clipper2Lib::FillRule::NonZero);

        NestPath ans=NestPath();
        for(int i=0;i<solution.size();i++){
            Polygon *polygon=new Polygon();
            Clipper2Lib::PathD path=solution[i];
            for(int j=0;j<path.size();j++){
                Clipper2Lib::PointD point=path[j];
                Point p=Point(point.x,point.y);
                polygon->add(p);
            }
            ans.push_back(polygon);
        }
        return ans;
    }
    static NestPath differencePolygons(NestPath& bin,NestPath& nfp){
        //获取bin的，nfp以外的区域
        Clipper2Lib::PathsD subject, clip, solution;
        for(int i=0;i<bin.size();i++){
            Polygon *polygon=bin[i];
            std::vector<double> pointVector=std::vector<double>();
            if(ccw(polygon)){
                for(int k=0;k<polygon->lenth;k++){
                    pointVector.push_back(polygon->getx(k));
                    pointVector.push_back(polygon->gety(k));
                }
            }else{
                for(int k=polygon->lenth-1;k>=0;k--){
                    pointVector.push_back(polygon->getx(k));
                    pointVector.push_back(polygon->gety(k));
                }
            }
            subject.push_back(Clipper2Lib::MakePathD(pointVector));
        }
        for(int i=0;i<nfp.size();i++){
            Polygon *polygon=nfp[i];
            std::vector<double> pointVector=std::vector<double>();
            for(int k=0;k<polygon->lenth;k++){
                pointVector.push_back(polygon->getx(k));
                pointVector.push_back(polygon->gety(k));
            }
            clip.push_back(Clipper2Lib::MakePathD(pointVector));
        }
        solution = Clipper2Lib::Difference(subject, clip, Clipper2Lib::FillRule::NonZero);

        NestPath ans=NestPath();
        for(int i=0;i<solution.size();i++){
            Polygon *polygon=new Polygon();
            Clipper2Lib::PathD path=solution[i];
            for(int j=0;j<path.size();j++){
                Clipper2Lib::PointD point=path[j];
                Point p=Point(point.x,point.y);
                polygon->add(p);
            }
            ans.push_back(polygon);
        }
        return ans;
    }

    static double areaPolygons(NestPath path){
        // todo: 是否可以计算出带孔洞多边形的面积
        Clipper2Lib::PathsD pathsD;
        for(int i=0;i<path.size();i++){
            Polygon *polygon=path[i];
            std::vector<double> pointVector=std::vector<double>();
            for(int k=0;k<polygon->lenth;k++){
                pointVector.push_back(polygon->getx(k));
                pointVector.push_back(polygon->gety(k));
            }
            pathsD.push_back(Clipper2Lib::MakePathD(pointVector));
        }
        return fabs(Clipper2Lib::Area(pathsD));
    }
};



#endif //BIN_PACKING_CLIPPERUTIL_H
